
#ifndef IO_H_
#define IO_H_

#include <string>
#include "status.hxx"


//for sequential reading, not thread-safe
class SequentialReader {

protected:
    std::string m_filename;

public:
    SequentialReader(const std::string& fileName)
        :m_filename(fileName)
    { }
    virtual ~SequentialReader(){}

    virtual DbStatus init() = 0;

    virtual DbStatus read(uint32_t n, std::string& result) = 0;
    virtual DbStatus skip(uint64_t n) = 0;
    virtual DbStatus getCurPos(uint64_t& pos) = 0;
    virtual DbStatus close() = 0;

private:
    // No copying allowed
    SequentialReader(const SequentialReader&);
    void operator=(const SequentialReader&); 
};

class PosixSequentialReader : public SequentialReader {

private:
	FILE* m_file;
	
public:
    PosixSequentialReader(const std::string& fname);
    ~PosixSequentialReader();

    DbStatus init();
    DbStatus read(uint32_t n, std::string& result);
    DbStatus skip(uint64_t n);
    DbStatus getCurPos(uint64_t& pos);
    DbStatus close();
};


// for randomly reading/writing the contents of a file.
class RandomAccessFile {
public:
    RandomAccessFile() { }
    virtual ~RandomAccessFile(){}

    virtual DbStatus init() = 0;	
    virtual DbStatus read(uint64_t offset, uint32_t n, std::string& result) = 0;
    virtual DbStatus read(uint64_t offset, uint32_t n, char* buf, uint32_t buflen, uint32_t& dlen) = 0;
    virtual DbStatus write(uint64_t offset, const std::string& data) = 0;
    virtual DbStatus write(uint64_t offset, const char* buf, uint32_t n) = 0;
    virtual DbStatus close() = 0;
    virtual DbStatus flush() = 0;
    virtual DbStatus dropCache() = 0;

private:
    // No copying allowed
    RandomAccessFile(const RandomAccessFile&);
    void operator=(const RandomAccessFile&);
};

// for sequential writing.  
class SequentialWriter {

public:
    SequentialWriter(){}   
    //SequentialWriter(const std::string& file) { }
    virtual ~SequentialWriter(){}

    virtual DbStatus init() = 0;

    virtual DbStatus append(const std::string& data) = 0;
    virtual DbStatus append(const char* buf, int len) = 0;
    virtual DbStatus close() = 0;
    virtual DbStatus flush() = 0;

    virtual uint32_t getNextOffset() = 0;

 private:
  // No copying allowed
  SequentialWriter(const SequentialWriter&);
  void operator=(const SequentialWriter&);
};


class PosixMmapSequentialWriter : public SequentialWriter {
private:
    std::string m_filename;
    int m_fd;
    uint32_t m_pageSize;

    //map size
    uint32_t m_mapSize;       

    //pointer to mapped memory buffer
    char* m_base;          

    //where to write next
    uint32_t m_dst;      

    //the offset synced up to
    uint32_t m_lastSync;      

    //offset m_base corresponds to in file
    uint64_t m_fileOffset;  

    //should we run fsync 
    bool m_shouldSync;

    //if false, sugget OS can release the buffer cache for the file
    bool m_oscache;

public:
    PosixMmapSequentialWriter(const std::string& fname, 
                              uint32_t page_size,
                              uint64_t offset, 
                              bool oscache);
    PosixMmapSequentialWriter(const std::string& fname, 
                              uint32_t page_size, 
                              bool oscache);
    ~PosixMmapSequentialWriter();

    DbStatus init();

    DbStatus append(const std::string& data);
    DbStatus append(const char* buf, int len);
    DbStatus close();
    DbStatus flush();

    uint32_t getNextOffset();

private:
    // Round down x to a multiple of y
    uint32_t rounddown(uint32_t x, uint32_t y);

    //unmap the current buffer
    bool unmap();

    //map a new buffer
    bool map();
};


class PosixRandomAccessFile : public RandomAccessFile {

private:
	int m_fd;
	std::string m_filename;

public:
	PosixRandomAccessFile(const std::string& fname);
	~PosixRandomAccessFile();

    DbStatus init();
    DbStatus read(uint64_t offset, uint32_t n, std::string& result);
    DbStatus read(uint64_t offset, uint32_t n, char* buf, uint32_t buflen, uint32_t& dlen);
    DbStatus write(uint64_t offset, const std::string& data);
    DbStatus write(uint64_t offset, const char* buf, uint32_t n);
    DbStatus close();
    DbStatus flush();
    DbStatus dropCache();

};


class PosixMmapRandomAccessFile : public RandomAccessFile {

private:
    int m_fd;
    std::string m_filename;
    uint32_t m_mapSize;
    char* m_base;

public:
    PosixMmapRandomAccessFile(const std::string& fname, uint32_t size);
    ~PosixMmapRandomAccessFile();

    DbStatus init();
    DbStatus read(uint64_t offset, uint32_t n, std::string& result);
    DbStatus read(uint64_t offset, uint32_t n, char* buf, uint32_t buflen, uint32_t& dlen);
    DbStatus write(uint64_t offset, const std::string& data);
    DbStatus write(uint64_t offset, const char* buf, uint32_t n);
    DbStatus close();
    DbStatus flush();
    DbStatus dropCache();

};


#endif
